package com.ruoyi.flow.flow.workflow;

import com.ruoyi.flow.activiti.domain.editor.model.Model;
import com.ruoyi.flow.common.utils.DateUtils;
import com.ruoyi.flow.common.utils.SpringContextHolder;
import com.ruoyi.flow.flow.FlowConstants;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.MultiInstanceLoopCharacteristics;
import org.activiti.engine.HistoryService;
import org.activiti.engine.history.*;
import org.activiti.engine.identity.Group;
import org.activiti.engine.impl.persistence.entity.IdentityLinkEntity;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @Author: wtian
 * @Description:
 * @Date: Created in 13:59 2018/8/8
 * @Version: 1.0
 */
public class FlowTask {
    private static final Logger LOGGER = LoggerFactory.getLogger(FlowTask.class);
    @OrderRef(queryClass = {HistoricProcessInstanceQuery.class, HistoricTaskInstanceQuery.class}, methodName = {"orderByProcessDefinitionId", "orderByProcessDefinitionId"})
    private String processDefinitionId;
    private String processInstanceId;
    private String taskId;
    /**
     * 任务名称
     */
    @OrderRef(queryClass = {TaskQuery.class, HistoricTaskInstanceQuery.class}, methodName = {"orderByTaskName", "orderByTaskName"})
    private String taskName;
    private String businessKey;
    /**
     * 模型名称
     */
    private String modelName;
    /**
     * 流程实例创建用户
     */
    private FlowProfile createUser;
    @OrderRef(queryClass = {HistoricProcessInstanceQuery.class, HistoricTaskInstanceQuery.class, TaskQuery.class}, methodName = {"orderByProcessInstanceStartTime", "orderByTaskCreateTime", "orderByTaskCreateTime"})
    private String createTimeStr;
    /**
     * 流程变量
     */
    private Map<String, Object> variables;
    /**
     * 节点类型
     */
    private String activityType;
    /**
     * 流程开始时间
     */
    @OrderRef(queryClass = {HistoricTaskInstanceQuery.class}, methodName = {"orderByHistoricTaskInstanceStartTime"})
    private String startTimeStr;
    /**
     * 流程结束时间 或者 任务结束时间
     */
    @OrderRef(queryClass = {HistoricTaskInstanceQuery.class}, methodName = {"orderByHistoricTaskInstanceEndTime"})
    private String endTimeStr;
    /**
     * 办理意见
     */
    private String comment;
    /**
     * 任务拥有者。如果流程分配个用户A办理，用户A委托用户B办理，那么owner就是就是用户A，asignee就是用户B
     */
    private FlowProfile owner;
    /**
     * 任务办理者
     */
    private FlowProfile assignee;
    /**
     * 任务候选人
     */
    private List<FlowProfile> candicateUsers;
    private List<Group> candicateGroups;
    /**
     * 任务/流程是否执行完成
     */
    private FlowConstants.FlowStatus status;
    /**
     * 截至日期
     */
    private String dueDateStr;
    /**
     * 执行ID
     */
    private String executionId;

    /**
     * 任务是否是多实例
     */
    private boolean multiInstance;

    private List<String> counterSignerIds;
    private List<FlowProfile> countSigners;

    private boolean delegated;

    public FlowTask(TaskEntity task) {
        try {
            this.taskId = task.getId();
            this.taskName = task.getName();
            this.processDefinitionId = task.getProcessDefinitionId();
            this.processInstanceId = task.getProcessInstanceId();
            ProcessInstance processInstance = FlowService.instanceService().getProcessInstanceByInstanceId(this.processInstanceId);
            if (processInstance != null) {
                this.businessKey = processInstance.getBusinessKey();
                this.createUser = new FlowProfile(processInstance.getStartUserId());
            }
            this.createTimeStr = DateUtils.format(task.getCreateTime(), DateUtils.DEFAULT_PATTERN);
            this.variables = FlowService.taskService().getTaskVariables(task.getId());
            this.modelName = FlowService.modelService().getModel(FlowService.definitionService().getProcessDefinitionByTaskId(task.getId()).getKey()).getName();
            this.activityType = FlowService.activityService().getFlowNode(task.getId()).getClass().getSimpleName();
            this.assignee = new FlowProfile(StringUtils.equals(activityType, "startEvent") ? this.createUser.getUserId() : task.getAssignee());
            this.owner = new FlowProfile(task.getOwner());
            try {
                List<IdentityLinkEntity> identityLinkEntities = task.getIdentityLinks();
                this.candicateUsers = task.getCandidates().stream().filter(link -> Objects.nonNull(link) && StringUtils.isNotEmpty(link.getUserId())).map((link) -> Objects.nonNull(link) ? new FlowProfile(link.getUserId()) : null).collect(Collectors.toList());
                this.candicateGroups = task.getCandidates().stream().filter(link -> Objects.nonNull(link) && StringUtils.isNotEmpty(link.getGroupId())).map(link -> Objects.nonNull(link) ? FlowService.identityService().getGroupByGroupId(link.getGroupId()) : null).collect(Collectors.toList());
            } catch (Exception e) {
                LOGGER.warn("获取IdentityLinkEntity异常，自动忽略该异常");
            }
            this.variables = FlowService.taskService().getTaskVariables(this.taskId);
            this.dueDateStr = DateUtils.format(task.getDueDate(), DateUtils.DEFAULT_PATTERN);
            this.executionId = task.getExecutionId();
            this.status = FlowConstants.FlowStatus.UNFINISHED;
            if (task.isSuspended()) {
                this.status = FlowConstants.FlowStatus.SUSPENDED;
            }
            this.delegated = task.getDelegationState() == null ? false : true;
            MultiInstanceLoopCharacteristics characteristics = FlowService.activityService().getMultiInstanceLoopCharacteristics(taskId);
            setMultiInstanceCharacterists(characteristics);
        } catch (Exception e) {
            LOGGER.error("封装TaskEntity对象产生错误", e);
        }
    }

    public FlowTask(HistoricTaskInstance taskInstance) {
        try {
            this.taskId = taskInstance.getId();
            this.taskName = taskInstance.getName();
            this.processInstanceId = taskInstance.getProcessInstanceId();
            this.processDefinitionId = taskInstance.getProcessDefinitionId();
            ProcessInstance instance = FlowService.instanceService().getProcessInstanceByInstanceId(processInstanceId);
            ProcessDefinition definition = FlowService.definitionService().getProcessDefinitionByDefinitionId(processDefinitionId);
            if (instance != null) {
                this.businessKey = instance.getBusinessKey();
                this.processInstanceId = instance.getId();
                this.createUser = new FlowProfile(instance.getStartUserId());
            }
            if (definition != null) {
                this.processDefinitionId = definition.getId();
                Model model = FlowService.modelService().getModel(definition.getKey());
                this.modelName = model.getName();
            }
            this.createTimeStr = DateUtils.format(taskInstance.getStartTime(), DateUtils.DEFAULT_PATTERN);
            this.startTimeStr = DateUtils.format(taskInstance.getStartTime(), DateUtils.DEFAULT_PATTERN);
            this.endTimeStr = DateUtils.format(taskInstance.getEndTime(), DateUtils.DEFAULT_PATTERN);
            if (taskInstance.getDueDate() != null) {
                this.dueDateStr = DateUtils.format(taskInstance.getDueDate(), DateUtils.DEFAULT_PATTERN);
            }
            FlowNode flowNode = FlowService.activityService().getFlowNode(taskInstance.getId());
            this.activityType = flowNode == null ? "" : flowNode.getClass().getSimpleName();
            this.assignee = new FlowProfile(StringUtils.equals(activityType, "startEvent") ? this.createUser.getUserId() : taskInstance.getAssignee());
            this.owner = new FlowProfile(taskInstance.getOwner());
            Comment cmt = FlowService.taskService().getTaskComment(this.taskId);
            if (cmt != null) {
                this.comment = cmt.getFullMessage();
            }
            this.variables = FlowService.taskService().getTaskVariables(this.taskId);
            this.executionId = taskInstance.getExecutionId();
            TaskEntity task = FlowService.taskService().getTaskByTaskId(taskId);
            if (task != null) {
                this.delegated = task.getDelegationState() == null ? false : true;
            }
            this.status = taskInstance.getEndTime() == null ? FlowConstants.FlowStatus.UNFINISHED : FlowConstants.FlowStatus.FINISHED;
            MultiInstanceLoopCharacteristics characteristics = FlowService.activityService().getMultiInstanceLoopCharacteristicsByExecution(executionId);
            setMultiInstanceCharacterists(characteristics);
        } catch (Exception e) {
            LOGGER.error("封装HistoricTaskInstance对象产生错误", e);
        }
    }

    public FlowTask(HistoricProcessInstance instance) {
            this.businessKey = instance.getBusinessKey();
            this.processInstanceId = instance.getId();
            ProcessDefinitionEntity processDefinitionEntity = null;
            processDefinitionEntity = FlowService.definitionService().getProcessDefinitionByDefinitionId(instance.getProcessDefinitionId());
            if (processDefinitionEntity != null) {
                this.modelName = FlowService.modelService().getModel(processDefinitionEntity.getKey()).getName();
            }
            this.processDefinitionId = instance.getProcessDefinitionId();
            this.createUser = new FlowProfile(instance.getStartUserId());
            this.startTimeStr = DateUtils.format(instance.getStartTime(), DateUtils.DEFAULT_PATTERN);
            boolean finished = FlowService.instanceService().getProcessInstanceByInstanceId(this.processInstanceId) == null ? true : false;
            if (finished) {
                this.status = FlowConstants.FlowStatus.FINISHED;
            } else {
                this.status = FlowConstants.FlowStatus.UNFINISHED;
            }
            ProcessInstance processInstance = FlowService.instanceService().getProcessInstanceByInstanceId(instance.getId());
            if (processInstance != null && processInstance.isSuspended()) {
                this.status = FlowConstants.FlowStatus.SUSPENDED;
            }
            this.endTimeStr = DateUtils.format(instance.getEndTime(), DateUtils.DEFAULT_PATTERN);
            this.variables = FlowService.instanceService().getStartVariables(instance.getId());
    }

    /**
     * 封装流程历史任务对象
     *
     * @param activityInstance
     */
    public FlowTask(HistoricActivityInstance activityInstance) {
        HistoryService historyService = SpringContextHolder.getBean("historyService");
        this.taskName = activityInstance.getActivityName();
        ProcessInstance instance = FlowService.instanceService().getProcessInstanceByInstanceId(activityInstance.getProcessInstanceId());
        if (instance == null) {//如果流程办结，这里是查不到流程实例的，只能查到历史流程实例
            HistoricProcessInstance historicProcessInstance = FlowService.instanceService().getHistoricProcessInstanceByInstanceId(activityInstance.getProcessInstanceId());
            this.businessKey = historicProcessInstance.getBusinessKey();
            this.processInstanceId = historicProcessInstance.getId();
            this.modelName = FlowService.modelService().getModel(historicProcessInstance.getProcessDefinitionKey()).getName();
            this.createUser = new FlowProfile(historicProcessInstance.getStartUserId());
        } else {
            this.businessKey = instance.getBusinessKey();
            this.processInstanceId = instance.getId();
            this.modelName = FlowService.modelService().getModel(instance.getProcessDefinitionKey()).getName();
            this.createUser = new FlowProfile(instance.getStartUserId());
        }
        this.processDefinitionId = activityInstance.getProcessDefinitionId();
        this.startTimeStr = DateUtils.format(activityInstance.getStartTime(), DateUtils.DEFAULT_PATTERN);
        this.endTimeStr = DateUtils.format(activityInstance.getEndTime(), DateUtils.DEFAULT_PATTERN);
        boolean finished = activityInstance.getEndTime() == null ? false : true;
        if (finished) {
            this.status = FlowConstants.FlowStatus.FINISHED;
        } else {
            this.status = FlowConstants.FlowStatus.UNFINISHED;
        }
        //获取任务办理用户
        this.activityType = activityInstance.getActivityType();
        this.assignee = new FlowProfile(StringUtils.equals(activityType, "startEvent") ? this.createUser.getUserId() : activityInstance.getAssignee());
        Comment cmt = FlowService.taskService().getTaskComment(activityInstance.getTaskId());
        if (cmt != null) {
            this.comment = cmt.getFullMessage();
        }
        this.executionId = activityInstance.getExecutionId();
        MultiInstanceLoopCharacteristics characteristics = FlowService.activityService().getMultiInstanceLoopCharacteristicsByExecution(executionId);
        setMultiInstanceCharacterists(characteristics);
    }

    public String getProcessDefinitionId() {
        return processDefinitionId;
    }

    public void setProcessDefinitionId(String processDefinitionId) {
        this.processDefinitionId = processDefinitionId;
    }

    public String getProcessInstanceId() {
        return processInstanceId;
    }

    public void setProcessInstanceId(String processInstanceId) {
        this.processInstanceId = processInstanceId;
    }

    public String getTaskId() {
        return taskId;
    }

    public void setTaskId(String taskId) {
        this.taskId = taskId;
    }

    public String getTaskName() {
        return taskName;
    }

    public void setTaskName(String taskName) {
        this.taskName = taskName;
    }

    public String getBusinessKey() {
        return businessKey;
    }

    public void setBusinessKey(String businessKey) {
        this.businessKey = businessKey;
    }

    public String getModelName() {
        return modelName;
    }

    public void setModelName(String modelName) {
        this.modelName = modelName;
    }

    public FlowProfile getCreateUser() {
        return createUser;
    }

    public void setCreateUser(FlowProfile createUser) {
        this.createUser = createUser;
    }

    public String getCreateTimeStr() {
        return createTimeStr;
    }

    public void setCreateTimeStr(String createTimeStr) {
        this.createTimeStr = createTimeStr;
    }

    public Map<String, Object> getVariables() {
        return variables;
    }

    public void setVariables(Map<String, Object> variables) {
        this.variables = variables;
    }

    public String getActivityType() {
        return activityType;
    }

    public void setActivityType(String activityType) {
        this.activityType = activityType;
    }

    public String getStartTimeStr() {
        return startTimeStr;
    }

    public void setStartTimeStr(String startTimeStr) {
        this.startTimeStr = startTimeStr;
    }

    public String getEndTimeStr() {
        return endTimeStr;
    }

    public void setEndTimeStr(String endTimeStr) {
        this.endTimeStr = endTimeStr;
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public FlowProfile getOwner() {
        return owner;
    }

    public void setOwner(FlowProfile owner) {
        this.owner = owner;
    }

    public FlowProfile getAssignee() {
        return assignee;
    }

    public void setAssignee(FlowProfile assignee) {
        this.assignee = assignee;
    }

    public List<FlowProfile> getCandicateUsers() {
        return candicateUsers;
    }

    public void setCandicateUsers(List<FlowProfile> candicateUsers) {
        this.candicateUsers = candicateUsers;
    }

    public List<Group> getCandicateGroups() {
        return candicateGroups;
    }

    public void setCandicateGroups(List<Group> candicateGroups) {
        this.candicateGroups = candicateGroups;
    }

    public FlowConstants.FlowStatus getStatus() {
        return status;
    }

    public void setStatus(FlowConstants.FlowStatus status) {
        this.status = status;
    }

    public String getDueDateStr() {
        return dueDateStr;
    }

    public void setDueDateStr(String dueDateStr) {
        this.dueDateStr = dueDateStr;
    }

    public String getExecutionId() {
        return executionId;
    }

    public void setExecutionId(String executionId) {
        this.executionId = executionId;
    }

    public boolean isMultiInstance() {
        return multiInstance;
    }

    public void setMultiInstance(boolean multiInstance) {
        this.multiInstance = multiInstance;
    }

    public List<String> getCounterSignerIds() {
        return counterSignerIds;
    }

    public void setCounterSignerIds(List<String> counterSignerIds) {
        this.counterSignerIds = counterSignerIds;
    }

    public List<FlowProfile> getCountSigners() {
        return countSigners;
    }

    public void setCountSigners(List<FlowProfile> countSigners) {
        this.countSigners = countSigners;
    }

    public boolean isDelegated() {
        return delegated;
    }

    public void setDelegated(boolean delegated) {
        this.delegated = delegated;
    }

    private void setMultiInstanceCharacterists(MultiInstanceLoopCharacteristics characterists) {
        if (characterists != null) {
            this.multiInstance = true;
            String varName = characterists.getInputDataItem();
            if (variables != null && variables.containsKey(varName)) {
                List<String> counterSignerIds = (List<String>) variables.get(varName);
                this.counterSignerIds = counterSignerIds;
                this.countSigners = counterSignerIds.stream().map((userid) -> new FlowProfile(userid)).collect(Collectors.toList());
            }
        }
    }
}
